package events;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.util.*;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

/**
 * @author Oliver Behncke
 *
 */

public class Events {

	private static final String JAXBCONTEXT = "events";
        
	private Events(){}
	
	/**
	 * Private helper function that returns the root element of the logfile
	 * 
	 * @return Simulation
	 * @throws JAXBException
	 */
	private static Simulation getSimulation(String logfilepath) throws JAXBException{
		JAXBContext context = JAXBContext.newInstance(JAXBCONTEXT);
		Unmarshaller unmarshaller = context.createUnmarshaller();
		return (Simulation) unmarshaller.unmarshal(new File(logfilepath));
	}
	
	/**
	 * Save an event to the logfile
	 * 
	 * @param event
	 * @throws JAXBException
	 * @throws FileNotFoundException
	 */
	public static void saveEvent(Event event, String logfilepath) throws JAXBException, FileNotFoundException{
		JAXBContext context = JAXBContext.newInstance(JAXBCONTEXT);
		Simulation simulation = getSimulation(logfilepath);
		List<Event> list=simulation.getPriceOrCostOrMail();
		list.add(event);
		Marshaller marshaller = context.createMarshaller();
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
				   new Boolean(true));
		marshaller.marshal(simulation, new FileOutputStream(logfilepath));
	}

	
	/**
	 * Get a list of all events in log file
	 * 
	 * @return
	 * @throws JAXBException
	 */
	public static List<Event> getEvents(String logfilepath) throws JAXBException{
            List<Event> result=new ArrayList<Event>(getSimulation(logfilepath).getPriceOrCostOrMail());
            Collections.sort(result);
            return result;
	}

        public static String getJAXBContext(){
            return JAXBCONTEXT;
        }
	
	/**
	 * Create a new CostType event
	 * 
	 * @param company
	 * @param to
	 * @param from
	 * @param type
	 * @param weightcost
	 * @param volumecost
	 * @param maxWeight
	 * @param maxVolume
	 * @param duration
	 * @param frequency
	 * @param day
	 * @return CostType
	 */
	public static CostType createCostType(String company,String to,String from,String type,int weightcost,int volumecost,int maxWeight, int maxVolume, int duration,int frequency, String day, long date){
		CostType result=new CostType();
	    result.setCompany(company);
	    result.setTo(to);
	    result.setFrom(from);
	    result.setType(type);
	    result.setWeightcost(BigInteger.valueOf(weightcost));
	    result.setVolumecost(BigInteger.valueOf(volumecost));
	    result.setMaxWeight(BigInteger.valueOf(maxWeight));
	    result.setMaxVolume(BigInteger.valueOf(maxVolume));
	    result.setDuration(BigInteger.valueOf(duration));
	    result.setFrequency(BigInteger.valueOf(frequency));
	    result.setDay(day);
	    result.setDate(date);
		return result;
	}
	/**
	 * Create a new DicontinueType event
	 * 
	 * @param to
	 * @param from
	 * @param company
	 * @param type
	 * @param date
	 * @return
	 */
	public static DiscontinueType createDiscontinueType(String to, String from, String company, String type, long routedate, long date){
		DiscontinueType result=new DiscontinueType();
	    result.setTo(to);
	    result.setFrom(from);
	    result.setCompany(company);
	    result.setType(type);
	    result.setDate(date);
            result.setRoutedate(routedate);
		return result;
	}
	
	/**
	 * Create a new MailType event
	 * 
	 * @param day
	 * @param to
	 * @param from
	 * @param weight
	 * @param volume
	 * @param priority
	 * @param date
	 * @return
	 */
	public static MailType createMailType(String day, String to, String from, int weight, int volume, String priority, long date){
		MailType result=new MailType();
	    result.setDay(day);
	    result.setTo(to);
	    result.setFrom(from);
	    result.setWeight(BigInteger.valueOf(weight));
	    result.setVolume(BigInteger.valueOf(volume));
	    result.setPriority(priority);
	    result.setDate(date);
		return result;
	}
	
	/**
	 * Create a new PriceType event
	 * 
	 * @param to
	 * @param from
	 * @param priority
	 * @param weightcost
	 * @param volumecost
	 * @param date
	 * @return
	 */
	public static PriceType createPriceType(String to, String from, String priority, int weightcost, int volumecost, long date){
		PriceType result=new PriceType();
	    result.setTo(to);
	    result.setFrom(from);
	    result.setPriority(priority);
	    result.setWeightcost(BigInteger.valueOf(weightcost));
	    result.setVolumecost(BigInteger.valueOf(volumecost));
	    result.setDate(date);
	    return result;
		
	}
	
	/**
	 * Create a new TimelimitType event
	 * 
	 * @param to
	 * @param from
	 * @param priority
	 * @param maxDeliveryTime
	 * @param date
	 * @return
	 */
	public static TimelimitType createTimelimitType(String to, String from, String priority, int maxDeliveryTime, long date){
		TimelimitType result=new TimelimitType();
		result.setFrom(from);
		result.setTo(to);
		result.setMaxDeliveryTime(BigInteger.valueOf(maxDeliveryTime));
		result.setPriority(priority);
		result.setDate(date);
		return result;
	}
        
        /**
        * Get the CostType objects from a list of Events that are still valid
        * @param events List<Event>
        * @return List of CostType objects that haven't been discontinued
        * @throws JAXBException
        */
        public static List<CostType> getActiveCostTypes(List<Event> events) {
            List<CostType> result=new LinkedList<CostType>();
            List<DiscontinueType> discontinueTypes=getDiscontinueTypes(events);
            for(Event e: events){
            if(e instanceof CostType){
                CostType c=(CostType) e;
                boolean discontinued=false;
                DiscontinueType toRemove=null;
                for(DiscontinueType d:discontinueTypes){
                    if(!discontinued && d.getCompany().equals(c.getCompany()) && d.getFrom().equals(c.getFrom()) && d.getTo().equals(c.getTo()) && d.getType().equals(c.getType()) && d.getDate()>c.getDate() && d.getRoutedate().equals(c.getDate())){
                        discontinued=true;
                        toRemove=d;
                    }
                }
                if(!discontinued){
                    result.add(c);
                    discontinueTypes.remove(toRemove);
                }
            }
            }
            return result;
        }
        
        
        /**
        * Get all PriceTypes that haven't been updated
        * @return
        * @throws JAXBException 
        */
        public static Set<PriceType> getPriceTypes(List<Event> events){
            Set<PriceType> result=new HashSet<PriceType>();
            for(Event e: events){
            if(e instanceof PriceType) {
                PriceType newP=(PriceType) e;
                for(PriceType p: new HashSet<PriceType>(result)){
                    if(p.getFrom().equals(newP.getFrom()) && p.getTo().equals(newP.getTo()) && p.getPriority().equals(newP.getPriority()) && newP.getDate()>p.getDate()) result.remove(p);
                }
                result.add(newP);
            } 
            }
            return result;
        }
        
       /**
        * Get all discontinue types from list of events
        * @return List of DiscontinueTypes
        * @throws JAXBException 
        */
        public static List<DiscontinueType> getDiscontinueTypes(List<Event> events) {
            List<DiscontinueType> result=new LinkedList<DiscontinueType>();
            for(Event e: events){
                if(e instanceof DiscontinueType)
                    result.add((DiscontinueType) e); 
            }
            return result;
        }
        
        /**
        * Get all mail types from list of events
        * @return List<MailType>
        * @throws JAXBException 
        */
        public static List<MailType> getMailTypes(List<Event> events) {
            List<MailType> result=new LinkedList<MailType>();
            for(Event e: events){
                if(e instanceof MailType)
                    result.add((MailType) e); 
            }
            return result;
        }
        
        
        
        public static List<Event> dropEventsAfter(List<Event> events, Event end){
            List<Event> result=new LinkedList<Event>();
            for(Event e: events){
                if(e.getDate() <= end.getDate()) result.add(e);
            }
            return result;
        }
        
        

        
}
